package ds.android.camera;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Toast;
import android.widget.ToggleButton;
import ds.android.camera.R.id;

public class CameraActivity extends Activity {
	private Camera mCamera;
	private CameraPreview mPreview;
	private int currentType = MEDIA_TYPE_IMAGE;
	private OnSeekBarChangeListener sb_zoom_change;
	private AutoFocusCallback mCamera_autofocus = new AutoFocusCallback() {

		@Override
		public void onAutoFocus(boolean success, Camera camera) {

		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.framelayout);

		LayoutInflater controlInflater = LayoutInflater.from(getBaseContext());
		viewControl = controlInflater.inflate(R.layout.control, null);
		LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT,
				LayoutParams.FILL_PARENT);

		addContentView(viewControl, layoutParams);

		mCamera = getCamera();

		if (mCamera == null) {
			return;
		}

		if (!mCamera.getParameters().isZoomSupported()) {
			try {
				SeekBar sb_zoom = (SeekBar) viewControl.findViewById(id.seekBar1);
				sb_zoom.setVisibility(SeekBar.INVISIBLE);
			} catch (Exception e1) {
			}
		} else {

			try {
				int maxZoom = mCamera.getParameters().getMaxZoom();
				showToast("Max Zoom: " + maxZoom);
				SeekBar sb_zoom = (SeekBar) viewControl.findViewById(id.seekBar1);
				sb_zoom.setMax(maxZoom - 1);
				sb_zoom.setOnSeekBarChangeListener(sb_zoom_change);
			} catch (Exception e) {
				try {
					SeekBar sb_zoom = (SeekBar) viewControl.findViewById(id.seekBar1);
					sb_zoom.setVisibility(SeekBar.INVISIBLE);
				} catch (Exception e1) {
				}
			}
		}

		sb_zoom_change = new OnSeekBarChangeListener() {

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {

			}

			@Override
			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
				mCamera.stopPreview();
				// showToast(progress + "");
				try {
					mCamera.getParameters().setZoom(progress + 1);
				} catch (Exception e) {
					showToast(e + "");
				}
				mCamera.startPreview();
			}
		};

		SeekBar sb_zoom = (SeekBar) viewControl.findViewById(id.seekBar1);
		if (sb_zoom != null) {
			sb_zoom.setOnSeekBarChangeListener(sb_zoom_change);
		} else {
			showToast("Seekbar is null");
		}

		mPreview = new CameraPreview(this, mCamera);
		FrameLayout preview = (FrameLayout) findViewById(id.camera_preview);
		preview.addView(mPreview);

	}

	private void showToast(String string) {
		Toast t = Toast.makeText(this, string, Toast.LENGTH_LONG);
		t.show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// menuItem_picture = menu.add(0, 1, 0, "Picture");
		// menuItem_picture.setEnabled(false);
		// menuItem_video = menu.add(0, 2, 0, "Video");
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case 1:
			currentType = MEDIA_TYPE_IMAGE;
			menuItem_picture.setEnabled(false);
			menuItem_video.setEnabled(true);
			break;
		case 2:
			currentType = MEDIA_TYPE_VIDEO;
			menuItem_picture.setEnabled(true);
			menuItem_video.setEnabled(false);
			break;
		}
		return false;
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
			if (currentType == MEDIA_TYPE_IMAGE) {
				if (useFlash() && cameraSupportsFlash()) {
					mCamera.getParameters().setFlashMode(
							mCamera.getParameters().getSupportedFlashModes().get(0));
				}
				mCamera.takePicture(null, null, mPicture);
			} else {
				if (recorderRunning) {
					recorderRunning = !recorderRunning;
					try {
						recorder.stop();
						recorder.reset();
						recorder.release();
						mCamera.lock();
						Toast t = Toast.makeText(this, "Video saved", Toast.LENGTH_LONG);
						t.show();
					} catch (IllegalStateException e) {
						Toast t = Toast.makeText(this, "Error:" + e.toString(), Toast.LENGTH_LONG);
						t.show();
					}
				} else {
					try {
						takeVideo();
					} catch (Exception e) {
						Toast t = Toast
								.makeText(this, "Error:" + e.getMessage(), Toast.LENGTH_LONG);
						t.show();
					}
				}
			}
		}
		return super.onKeyUp(keyCode, event);
	}

	private boolean cameraSupportsFlash() {
		Parameters parameters;
		try {
			parameters = mCamera.getParameters();
		} catch (Exception e) {
			return false;
		}
		List<String> supportedFlashModes;
		try {
			supportedFlashModes = parameters.getSupportedFlashModes();
		} catch (Exception e) {
			return false;
		}

		if (supportedFlashModes == null) {
			return false;
		}

		return !supportedFlashModes.isEmpty();
	}

	private boolean useFlash() {
		ToggleButton tb_flash = (ToggleButton) viewControl.findViewById(id.toggleButton_fl);
		return tb_flash.isChecked();
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (autofocus()) {
			if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER && currentType == MEDIA_TYPE_IMAGE) {
				mCamera.autoFocus(mCamera_autofocus);
			}
		}
		return super.onKeyDown(keyCode, event);
	}

	private boolean autofocus() {
		ToggleButton tb_af = (ToggleButton) viewControl.findViewById(id.toggleButton_af);
		return tb_af.isChecked();
	}

	private boolean recorderRunning = false;

	private void takeVideo() throws Exception {

		recorderRunning = !recorderRunning;

		try {
			mCamera.unlock();
		} catch (Exception e) {
		}

		recorder = new MediaRecorder();
		recorder.setCamera(mCamera);
		recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
		recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
		recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
		recorder.setPreviewDisplay(mPreview.getHolder().getSurface());
		File outputMediaFile = getOutputMediaFile(MEDIA_TYPE_VIDEO);
		recorder.setOutputFile(outputMediaFile.toString());
		recorder.prepare();
		recorder.start();
		Toast t = Toast.makeText(this, "Recording...", Toast.LENGTH_LONG);
		t.show();

	}

	private Camera getCamera() {
		Camera c = null;
		try {
			c = Camera.open();

			setCameraDisplayOrientation(0, c);

			Camera.Parameters params = c.getParameters();
			params.set("rotation", 90);
			c.setParameters(params);
		} catch (Exception e) {

		}

		return c;
	}

	@Override
	protected void onResume() {
		super.onResume();
		try {
			mCamera = Camera.open();
			mPreview.setCamera(mCamera);
		} catch (Exception e) {
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
		if (mCamera != null) {
			try {
				mPreview.setCamera(null);
				mCamera.release();
				mCamera = null;
			} catch (Exception e) {
			}
		}
	}

	public static final int MEDIA_TYPE_IMAGE = 1;
	public static final int MEDIA_TYPE_VIDEO = 2;

	private PictureCallback mPicture = new PictureCallback() {
		@Override
		public void onPictureTaken(byte[] data, Camera camera) {
			File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
			if (pictureFile == null) {
				return;
			}

			try {
				FileOutputStream fos = new FileOutputStream(pictureFile);
				fos.write(data);
				fos.close();
			} catch (FileNotFoundException e) {
			} catch (IOException e) {
			}
			try {
				mCamera.startPreview();
			} catch (Exception e) {
			}
		}
	};
	private MediaRecorder recorder;
	private MenuItem menuItem_picture;
	private MenuItem menuItem_video;
	private View viewControl;

	private static File getOutputMediaFile(int type) {
		File mediaStorageDir = null;

		if (type == MEDIA_TYPE_IMAGE) {
			mediaStorageDir = new File(
					Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
					"CameraDS");
		} else {
			mediaStorageDir = new File(
					Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES),
					"CameraDS");
		}
		if (!mediaStorageDir.exists()) {
			if (!mediaStorageDir.mkdirs()) {
				return null;
			}
		}
		String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
		File mediaFile;
		if (type == MEDIA_TYPE_IMAGE) {
			mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp
					+ ".jpg");
		} else if (type == MEDIA_TYPE_VIDEO) {
			mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp
					+ ".mp4");
		} else {
			return null;
		}
		return mediaFile;
	}

	public void setCameraDisplayOrientation(int cameraId, Camera camera) {
		Camera.CameraInfo info = new Camera.CameraInfo();
		Camera.getCameraInfo(cameraId, info);
		int rotation = getWindowManager().getDefaultDisplay().getRotation();
		int degrees = 0;
		switch (rotation) {
		case Surface.ROTATION_0:
			degrees = 0;
			break;
		case Surface.ROTATION_90:
			degrees = 90;
			break;
		case Surface.ROTATION_180:
			degrees = 180;
			break;
		case Surface.ROTATION_270:
			degrees = 270;
			break;
		}

		int result;
		if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
			result = (info.orientation + degrees) % 360;
			result = (360 - result) % 360; // compensate the mirror
		} else { // back-facing
			result = (info.orientation - degrees + 360) % 360;
		}
		camera.setDisplayOrientation(result);
	}

}